import * as fs from "fs";
import * as path from "path";
import { parse } from "@typescript-eslint/parser";
import { AST_NODE_TYPES, ESLintUtils, TSESTree } from "@typescript-eslint/utils";

const createRule = ESLintUtils.RuleCreator(
    name => `https://example.com/rule/${name}`,
);

function findClassDeclarationByName(className: string, ast: TSESTree.Program): TSESTree.ClassDeclaration | undefined {
    let classDeclaration = ast.body.find(
        (node) => {
            if (node.type === AST_NODE_TYPES.ExportNamedDeclaration) {
                node = node.declaration;
            }
            if (node.type === AST_NODE_TYPES.ClassDeclaration) {
                return node.id && node.id.type === AST_NODE_TYPES.Identifier && node.id.name === className;
            }
            return false;
        }
    );
    if (classDeclaration && classDeclaration.type === AST_NODE_TYPES.ExportNamedDeclaration) {
        classDeclaration = classDeclaration.declaration;
    }
    return classDeclaration as TSESTree.ClassDeclaration | undefined;
}

function findSuperClassByName(filename: string, node: TSESTree.ClassDeclaration | TSESTree.ClassExpression, className: string, ast: TSESTree.Program, depth: number = 0, depthList: boolean[] = []): boolean {
    if (!node.superClass) {
        return true;
    }
    if (node.superClass.type === "Identifier") {
        console.log(`${depth} ${depthList.length} ${node.superClass.name}`);
        if (node.superClass.name === className) {
            if (depthList.length == depth) {
                return true;
            } else {
                return false;
            }
        } else {
            const superClassName = node.superClass.name;
            let superClassNode = findClassDeclarationByName(superClassName, ast);
            if (!superClassNode) {
                let superfilepath = "";
                const bodyCount = ast.body.length;
                for (let i = 0; i < bodyCount; i++) {
                    const node = ast.body[i];
                    if (node.type == AST_NODE_TYPES.ImportDeclaration) {
                        const importCount = node.specifiers.length;
                        console.log(importCount);
                        console.log(node.source.value);
                        console.log(superClassName);
                        for (let j = 0; j < importCount; j++) {
                            const spec = node.specifiers[j];
                            if (spec.local.type === AST_NODE_TYPES.Identifier && spec.local.name === superClassName) {
                                superfilepath = node.source.value;
                                break;
                            }
                        }
                    }
                    if (superfilepath != "") {
                        break;
                    }
                }
                const filepath = path.dirname(filename);
                const superFilename = path.join(filepath, superfilepath + ".ts");
                console.log(filepath);
                console.log(superFilename);
                const fileContent = fs.readFileSync(superFilename, "utf-8");
                const superAst = parse(fileContent);
                superClassNode = findClassDeclarationByName(superClassName, superAst);
                if (superClassNode) {
                    depthList.push(true);
                    return findSuperClassByName(filename, superClassNode, className, superAst, depth, depthList);
                }
            }
            if (superClassNode) {
                depthList.push(true);
                return findSuperClassByName(filename, superClassNode, className, ast, depth, depthList);
            }
        }
    }
    return false;
}

function isSubclassOfEntity(filename: string, node: TSESTree.ClassDeclaration | TSESTree.ClassExpression, ast: TSESTree.Program, depth: number = 0): boolean {
    return findSuperClassByName(filename, node, "Entity", ast, depth);
}

// Type: RuleModule<"uppercase", ...>
const rule = createRule({
    create(context) {
        return {
            ClassDeclaration(node) {
                if (node.id != null && node.superClass) {
                    const filename = context.getPhysicalFilename();
                    const code = context.getSourceCode();
                    if (!isSubclassOfEntity(filename, node, code.ast)) {
                        context.report({
                            messageId: "multiple",
                            node: node.id,
                        });
                    }
                }
            }
        };
    },
    name: "entity-class-declaration",
    meta: {
        docs: {
            description: "实体类只能继承自Entity, 禁止多层继承.",
            recommended: "warn",
        },
        messages: {
            multiple: "禁止多层继承.",
        },
        type: "suggestion",
        schema: [],
    },
    defaultOptions: [],
});

module.exports = rule;